
// Copyright (C) Recursion Company. All rights reserved.

#include "../vol_base.h"
#include "../include/vol_service.h"

CVolRuntimeClass* CVolService::ms_pUserServiceProcessor = NULL;
SERVICE_STATUS_HANDLE CVolService::ms_hServiceStatusHandle = NULL;
DWORD CVolService::ms_dwLastStatus = SERVICE_STOPPED;

void CVolService::sInitMembers ()
{
    sInitMembers (NULL);
}

void CVolService::sInitMembers (CVolRuntimeClass* pUserServiceProcessor)
{
    ms_pUserServiceProcessor = pUserServiceProcessor;
    ms_dwLastStatus = SERVICE_STOPPED;
    ms_hServiceStatusHandle = NULL;
}

void CVolService::sSetServiceStatus (DWORD dwStatus)
{
    if (ms_hServiceStatusHandle != NULL)
    {
        SERVICE_STATUS status;
        ZERO_MEM (&status, sizeof (status));
        status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
        status.dwCurrentState = dwStatus;
        status.dwControlsAccepted = (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);

        SetServiceStatus (ms_hServiceStatusHandle, &status);
    }

    ms_dwLastStatus = dwStatus;
}

BOOL CVolService::sLogEvent (const TCHAR* szServiceName, const TCHAR* szEventMessage, WORD wEventType, WORD wEventCategory, DWORD dwEventID)
{
    if (IsEmptyStr (szEventMessage) || IsEmptyStr (szServiceName))
        return FALSE;

    const HANDLE hEventSource = RegisterEventSource (NULL, szServiceName);
    if (hEventSource == NULL)
        return FALSE;

    LPCTSTR aszStrings [1];
    aszStrings [0] = szEventMessage;
    const BOOL blSucceeded = ReportEvent (hEventSource, wEventType, wEventCategory, dwEventID, NULL, 1, 0, aszStrings, NULL);
    DeregisterEventSource (hEventSource);

    return blSucceeded;
}

BOOL CVolService::sIsServiceInstalled (const TCHAR* szServiceName)
{
    if (IsEmptyStr (szServiceName))
        return FALSE;

    BOOL blInstalled = FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM != NULL)
    {
        const SC_HANDLE hService = OpenService (hSCM, szServiceName, SERVICE_QUERY_CONFIG);

        if (hService != NULL)
        {
            CloseServiceHandle (hService);
            blInstalled = TRUE;
        }

        CloseServiceHandle (hSCM);
    }

    return blInstalled;
}

BOOL CVolService::sInstallService (const TCHAR* szServiceName, const TCHAR* szServiceDisplayName,
        const TCHAR* szServiceDesc, const TCHAR* szServiceFileName, BOOL blAutoStart, const TCHAR* szServiceStartupParam)
{
    if (IsEmptyStr (szServiceName) || sIsServiceInstalled (szServiceName))
        return FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
        return FALSE;

    TCHAR buf [MAX_PATH];
    if (IsEmptyStr (szServiceFileName))
    {
        buf [0] = '\0';
        if (GetModuleFileName (NULL, buf, MAX_PATH) == 0)
            return FALSE;
        szServiceFileName = buf;
    }
    szServiceFileName = SkipSpaces (szServiceFileName);

    CVolString strBuf1;
    if (*szServiceFileName != '\"' && _tcschr (szServiceFileName, ' ') != NULL)
    {
        strBuf1.AddChar ('\"');
        strBuf1.AddText (szServiceFileName);
        strBuf1.AddChar ('\"');
        szServiceFileName = strBuf1.GetText ();
    }

    CVolString strBuf2;
    if (IsEmptyStr (szServiceStartupParam) == FALSE)
    {
        strBuf2.AddText (szServiceFileName);
        strBuf2.AddChar (' ');
        strBuf2.AddText (szServiceStartupParam);
        szServiceFileName = strBuf2.GetText ();
    }

    const SC_HANDLE hService = ::CreateService (
            hSCM,
            szServiceName,
            (IsEmptyStr (szServiceDisplayName) ? szServiceName : szServiceDisplayName),
            SERVICE_ALL_ACCESS,
            SERVICE_WIN32_OWN_PROCESS,
            (blAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START),
            SERVICE_ERROR_NORMAL,
            szServiceFileName,
            NULL, NULL, _T (""), NULL, NULL);

    if (hService != NULL)
    {
        if (IsEmptyStr (szServiceDesc) == FALSE)
        {
            SERVICE_DESCRIPTION sd;
            sd.lpDescription = (TCHAR*)szServiceDesc;
            ChangeServiceConfig2 (hService, SERVICE_CONFIG_DESCRIPTION, &sd);
        }

        CloseServiceHandle (hService);
    }

    CloseServiceHandle (hSCM);
    return (hService != NULL);
}

BOOL CVolService::sUninstallService (const TCHAR* szServiceName)
{
    if (IsEmptyStr (szServiceName))
        return FALSE;

    BOOL blSucceeded = FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM != NULL)
    {
        const SC_HANDLE hService = OpenService (hSCM, szServiceName, (SERVICE_STOP | DELETE));
        if (hService != NULL)
        {
            SERVICE_STATUS status;
            ControlService (hService, SERVICE_CONTROL_STOP, &status);

            blSucceeded = DeleteService (hService);

            CloseServiceHandle (hService);
        }

        CloseServiceHandle (hSCM);
    }

    return blSucceeded;
}

BOOL CVolService::sStartService (const TCHAR* szServiceName, const CMStringArray* psaryArgs)
{
    if (IsEmptyStr (szServiceName))
        return FALSE;

    BOOL blSucceeded = FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL,NULL,SC_MANAGER_CONNECT);
    if (hSCM != NULL)
    {
        const SC_HANDLE hService = OpenService (hSCM, szServiceName, SERVICE_START);
        if (hService != NULL)
        {
            /* CMStringArray sary;
            sary.Add (szServiceName);
            if (psaryArgs != NULL)
                sary.Append (*psaryArgs);

            // the first argument (lpServiceArgVectors[0]) is the name of the service, followed by any additional 
            // arguments (lpServiceArgVectors[1] through lpServiceArgVectors[dwNumServiceArgs-1]).
            blSucceeded = StartService (hService, (DWORD)sary.GetCount (), (const TCHAR**)sary.GetData ()); */

            DWORD dwArgCount = 0;
            LPCTSTR* lpServiceArgVectors = NULL;
            
            if (psaryArgs != NULL && psaryArgs->GetCount () > 0)
            {
                dwArgCount = (DWORD)psaryArgs->GetCount();
                lpServiceArgVectors = (const TCHAR**)psaryArgs->GetData();
            }
            
            blSucceeded = StartService (hService, dwArgCount, lpServiceArgVectors);
            CloseServiceHandle (hService);
        }

        CloseServiceHandle (hSCM);
    }

    return blSucceeded;
}

BOOL CVolService::sStopService (const TCHAR* szServiceName)
{
    if (IsEmptyStr (szServiceName))
        return FALSE;

    BOOL blSucceeded = FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM != NULL)
    {
        const SC_HANDLE hService = OpenService (hSCM, szServiceName, (SERVICE_STOP | SERVICE_QUERY_STATUS));
        if (hService != NULL)
        {
            SERVICE_STATUS status;
            if (QueryServiceStatus (hService, &status))
            {
                if (status.dwCurrentState != SERVICE_STOPPED)
                    blSucceeded = ControlService (hService, SERVICE_CONTROL_STOP, &status);
                else
                    blSucceeded = TRUE;
            }

            CloseServiceHandle (hService);
        }

        CloseServiceHandle (hSCM);
    }

    return blSucceeded;
}

BOOL CVolService::sPauseService (const TCHAR* szServiceName, BOOL blPause)
{
    if (IsEmptyStr (szServiceName))
        return FALSE;

    BOOL blSucceeded = FALSE;

    const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM != NULL)
    {
        const SC_HANDLE hService = OpenService (hSCM, szServiceName, (SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS));
        if (hService != NULL)
        {
            SERVICE_STATUS status;
            blSucceeded = ControlService (hService, (blPause ? SERVICE_CONTROL_PAUSE : SERVICE_CONTROL_CONTINUE), &status);

            CloseServiceHandle (hService);
        }

        CloseServiceHandle (hSCM);
    }

    return blSucceeded;
}

INT CVolService::sGetServiceStatus (const TCHAR* szServiceName)
{
    INT nStatus = 0;

    if (IsEmptyStr (szServiceName) == FALSE)
    {
        const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (hSCM != NULL)
        {
            const SC_HANDLE hService = OpenService (hSCM, szServiceName, SERVICE_QUERY_STATUS);
            if (hService != NULL)
            {
                SERVICE_STATUS status;
                if (QueryServiceStatus (hService, &status))
                    nStatus = (INT)status.dwCurrentState;

                CloseServiceHandle (hService);
            }

            CloseServiceHandle (hSCM);
        }
    }

    return nStatus;
}

LPQUERY_SERVICE_CONFIG CVolService::sGetServiceConfig (const TCHAR* szServiceName, CVolMem& memBuf)
{
    LPQUERY_SERVICE_CONFIG pConfig = NULL;

    if (IsEmptyStr (szServiceName) == FALSE)
    {
        const SC_HANDLE hSCM = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (hSCM != NULL)
        {
            const SC_HANDLE hService = OpenService (hSCM, szServiceName, SERVICE_QUERY_CONFIG);

            if (hService != NULL)
            {
                DWORD cbBytesNeeded = 0;
                if (QueryServiceConfig (hService, NULL, 0, &cbBytesNeeded) || GetLastError () == ERROR_INSUFFICIENT_BUFFER)
                {
                    pConfig = (LPQUERY_SERVICE_CONFIG)memBuf.Alloc ((INT_P)cbBytesNeeded);

                    if (QueryServiceConfig (hService, pConfig, cbBytesNeeded, &cbBytesNeeded) == FALSE)
                        pConfig = NULL;
                }

                CloseServiceHandle (hService);
            }

            CloseServiceHandle (hSCM);
        }
    }

    return pConfig;
}
